/*--------------------------------------------------------------------------
    This source file is part of Abraxas Game Engine
    For the latest info, see http://code.google.com/abraxasge/
    Copyright (C) 2009, Andrew LeRoy <aleroy@member.igda.org>
    ---

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA  02110-1301, USA.
--------------------------------------------------------------------------*/
#ifndef _Abx_Optional_hpp_
#define _Abx_Optional_hpp_

#include "AbxPrerequisites.hpp"
#include <new>
#include <assert.h>

namespace Abx
{
///////////////////////////////////////////////////////////////////////////////
// optional.h
//
// An isolation point for optionality, provides a way to define
// objects having to provide a special "null" state.
//
// In short:
//
// struct optional<T>
// {
//     bool m_bValid;
//
//    T m_data;
// };
//
//

    class _AbxExport Optional_empty { };

    template <unsigned long size>
    class _AbxExport Optional_base
    {
      public:
        // Default - invalid.

        Optional_base() : valid_(false) { }

        Optional_base& operator= (Optional_base const& t)
        {
            valid_ = t.valid_;
            return *this;
        }

        //Copy constructor
        Optional_base(Optional_base const& other) : valid_(other.valid_) 
        { }

        //utility functions
        bool const valid() const       { return valid_; }
        bool const invalid() const     { return !valid_; }

      protected:
        bool valid_;
        char data_[size];  // storage space for T
    };


    template <class T>
    class _AbxExport Optional : public Optional_base<sizeof(T)>
    {
    public:
        // Default - invalid.
        Optional() { }
        Optional(T const& t)  { construct(t); this->valid_ = (true); }
        Optional(Optional_empty const&) { }

        Optional& operator= (T const& t)
        {
            if (this->valid_)
            {
                *GetT() = t;
            }
            else
            {
                construct(t);
                this->valid_ = true;            // order important for exception safety.
            }
            return *this;
        }

        //Copy constructor
        Optional(Optional const& other)
        {   
            if (other.valid_)
            {
                construct(*other);
                this->valid_ = true;            // order important for exception safety.
            }
        }

        Optional& operator= (Optional const& other)
        {
            assert(! (this == & other));    // don't copy over self!
            if (this->valid_)
            {                               // first, have to destroy our original.
                this->valid_ = false;             // for exception safety if destroy() throws.
                                            // (big trouble if destroy() throws, though)
                destroy();
            }

            if (other.valid_)
            {
                construct(*other);
                this->valid_ = true;              // order vital.
            }
            return *this;
        }

        bool const operator== (Optional const& other) const
        {
            if ((! valid()) && (! other.valid())) { return true; }
            if (valid() ^ other.valid()) { return false; }
            return ((* * this) == (* other));
        }

        bool const operator< (Optional const& other) const
        {
            // equally invalid - not smaller.
            if ((! valid()) && (! other.valid()))   { return false; }

            // I'm not valid, other must be, smaller.
            if (! valid()) { return true; }

            // I'm valid, other is not valid, I'm larger
            if (! other.valid()) { return false; }

                return ((* * this) < (* other));
        }

        // Destructor
        ~Optional() { if (this->valid_) destroy(); }


        // Accessors.
        T const& operator* () const            { assert(this->valid_); return * GetT(); }
        T& operator* ()                        { assert(this->valid_); return * GetT(); }
        T const * const operator-> () const    { assert(this->valid_); return GetT();   }
        T    * const operator-> ()             { assert(this->valid_); return GetT();   }

        //This clears the value of this optional variable and makes it invalid once
        // again.
        void clear()
        {
            if (this->valid_)
            {
                this->valid_ = false;
                destroy();
            }
        }

        //utility functions
        bool const valid() const       { return this->valid_; }
        bool const invalid() const     { return !this->valid_; }


    private:
        T const * const GetT() const { return reinterpret_cast<T const * const>(this->data_); }
        T * const GetT()             { return reinterpret_cast<T * const>(this->data_);       }
        void construct(T const & t)  { new (GetT()) T(t);                               }
        void destroy()               { GetT()->~T();                                    }
    };


/*
        // Optional.cpp : Defines the entry point for the console application.
        //

        #include "stdafx.h"
        #include "optional.h"

        optional<int> Calculate()
        {
            optional<int> spline;
            spline = 10;                      // you assign values to optionals like this...
            spline = optional_empty();        // or you could give them the empty value
            spline.clear();                   // or you could clear them to make them invalid

            return spline;
        }

        int main(void)
        {
            /////////////////////////////////////////////////////////////////////////////////
            // Using optional<T>
            //
            optional<int> answer = Calculate();
            if (answer.valid())
            {
                // do my business...
            }
            return 0;
        }
*/
} // namespace Abx

#endif // _Abx_Optional_hpp_
